import {
	relative,
	resolve,
	dirname,
} from 'node:path';
import currentScript from '../util/currentScript.js';
import MyPromise from '../util/MyPromise.js';
//region 注入js用到的工具方法
function getBaseUrl(isAuto){
	if(isAuto&&document&&document.currentScript){
		return document.currentScript.src
			.replace(/#.*$/, '')
			.replace(/\?.*$/, '')
			.replace(/\/[^\/]+$/, '/');
	}
	return '';
}
function load(type,url,debug){
	var node;
	return new Promise(function(resolve,reject){
		if(type==='js'){
			node=document.createElement('script');
			node.src=url;
		}else{
			node=document.createElement('link');
			node.rel='stylesheet';
			node.href=url;
		}
		node.onload=resolve;
		node.onerror=reject;
		document.head.appendChild(node);
	}).finally(function(){
		debug&&console.log(
			'%c动态加载'+type+'%c'+url,
			'background:'+
			(
				type==='js'
					?'#eaa94d'
					:'#ea4c67'
			)+
			';padding:1px 4px;color:#444;border-radius:3px;',
			'padding:1px 8px;'
		);
		node.onerror=node.onload=null;
		if(type==='js'){
			node.parentNode&&node.parentNode.removeChild(node);
		}
	});
}
function serialLoad(baseUrl,urls,debug){
	var promise=new Promise(function(resolve){
		resolve();
	});
	for(var i=0;i<urls.length;++i){
		promise=promise.then(function(url){
			return load('js',url,debug);
		}.bind(null,baseUrl+urls[i]));
	}
	return promise;
}
//endregion

class PowerfulWebpackPlugin{
	constructor(config){
		this.config=config;
	}

	apply(compiler){
		const {webpack}=compiler;
		const {Compilation}=webpack;
		const {RawSource}=webpack.sources;
		compiler.hooks.thisCompilation.tap(PowerfulWebpackPlugin.name,(compilation) => {
			compilation.hooks.processAssets.tap({
				name:PowerfulWebpackPlugin.name,
				stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
			},(assets) => {
				//region 打包信息文件
				if(this.config.buildFile.emit){
					let content;
					if(Array.isArray(this.config.buildFile.content)){
						content=this.config.buildFile.content.join('\n');
					}else{
						content=this.config.buildFile.content||'';
					}
					compilation.emitAsset(
						this.config.buildFile.name,
						new RawSource(content),
					);
				}
				//endregion
				//region 生成文件
				const {emitFiles}=this.config;
				for(const [filename,content] of Object.entries(emitFiles)){
					if(content!=null){
						let finalContent;
						if(Array.isArray(content)){
							finalContent=content.join('\n');
						}else{
							finalContent=content||'';
						}
						compilation.emitAsset(
							filename,
							new RawSource(finalContent),
						);
					}
				}
				//endregion
				//region 单个入口注入js
				const {publicPath,path}=compilation.options.output;
				function emitEntry(entry,config){
					if(typeof config==='string'){
						config={
							filename:config,
							preLoad:[],
							debug:false,
						}
					}
					const filename=config.filename;
					const preLoad=config.preLoad??[];
					const debug=config.debug??false;
					const base=publicPath==='auto'
						?relative(
							resolve(path,dirname(filename)),
							path,
						)
						:publicPath;
					const preLoadCss=[];
					const preLoadJs=[];
					for(const file of preLoad){
						if(file.endsWith('.js')){
							preLoadJs.push(file);
						}else if(file.endsWith('.css')){
							preLoadCss.push(file);
						}
					}
					const jsArr=[];
					const cssArr=[];
					for(const chunk of compilation.entrypoints.get(entry).chunks){
						for(let file of chunk.files){
							if(file.endsWith('.js')){
								jsArr.push(base+file);
							}else if(file.endsWith('.css')){
								cssArr.push(base+file);
							}
						}
					}
					const content=[
`(${currentScript.toString()})(document);
(function(){
var Promise=(
${MyPromise.toString()}
)();
${load.toString()}
${serialLoad.toString()}
	function start(){
		var baseUrl=(
${getBaseUrl.toString()}
		)(${publicPath==='auto'});`,
		...preLoadCss.map((css) => `\t\tload('css','${css}',${debug});`),
		...cssArr.map((css) => `\t\tload('css',baseUrl+'${css}',${debug});`),
`		serialLoad('',${JSON.stringify(preLoadJs,null,'	')},${debug})
			.then(function(){
				return serialLoad(baseUrl,${JSON.stringify(jsArr,null,'	')},${debug});
			});
	}
	if(document.readyState==="loading"){
		document.addEventListener("DOMContentLoaded",start);
	}else{
		start();
	}
})();`
					].join('\n');
					compilation.emitAsset(
						filename,
						new RawSource(content),
					);
				}
				//endregion
				if(this.config.emitEntryInjectJs){
					for(const [entry,config] of Object.entries(this.config.emitEntryInjectJs)){
						emitEntry(entry,config);
					}
				}
			});
		});
	}
}

export default PowerfulWebpackPlugin;
